package com.ghck.server.service.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.collections4.CollectionUtils;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.DefaultTypedTuple;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.ghck.server.dao.FriendPatientEventDayMapper;
import com.ghck.server.exception.GhckException;
import com.ghck.server.exception.ErrorConstant;
import com.ghck.server.model.FriendPatientEventDay;
import com.ghck.server.model.query.FriendPatientEventDayQuery;
import com.ghck.server.model.request.PatientEventCategory;
import com.ghck.server.service.FriendPatientEventDayService;
import com.ghck.server.utils.JodaTimeUtil;
import com.ghck.server.utils.RedisKeyUtil;

/**
 * 亲友健康数据-每天只记录一条  <br>
 * t_friend_patient_event_day <br>
 * v1.0 2016-05-22
 */
@Service
@Transactional
public class FriendPatientEventDayServiceImpl implements FriendPatientEventDayService {
	private final static int LIMIT=8;
	private static final Logger logger = LoggerFactory.getLogger(FriendPatientEventDayServiceImpl.class);

	@Autowired
	private FriendPatientEventDayMapper friendPatientEventDayMapper;

	@Autowired
	private RedisTemplate<String, Object> template;


	/**
	 *根据ID删除 亲友健康数据-每天只记录一条
	 */
	@Override
	public Integer deleteFriendPatientEventDayById(Integer id){
		return friendPatientEventDayMapper.deleteById(id);
	}

	/**
	 *新增 亲友健康数据-每天只记录一条
	 */
	@Override
	@Transactional
	public Integer batchInsertFriendPatientEventDay(List<? extends FriendPatientEventDay> friendPatientEventDays){
		Integer count = friendPatientEventDayMapper.batchInsert(friendPatientEventDays);
		return count;
	}
	/**
	 *新增 亲友健康数据-每天只记录一条
	 */
	@Override
	@Transactional
	public Integer insertFriendPatientEventDay(FriendPatientEventDay domain){

		Integer friendId = domain.getFriendId();
		Integer categoryId = domain.getCategoryId();
		Date actionTime = domain.getActionTime();
		FriendPatientEventDay dayData =  friendPatientEventDayMapper.findByCategoryIdAndActionTime(friendId, categoryId, actionTime);
		Integer id = null;
		if (dayData!=null) {
			domain.setId(dayData.getId());
			updateFriendPatientEventDay(domain);
			id = domain.getId();
		}else{
			friendPatientEventDayMapper.insert(domain);
			id = domain.getId();
			//缓存
			HashOperations<String, String, FriendPatientEventDay> hashOperations = template.opsForHash();
			String key = RedisKeyUtil.friendLastPatientEventMapKey(friendId);
			FriendPatientEventDay pEvent =hashOperations.get(key, PatientEventCategory.getPatientEventCategoryByValue(categoryId).name());
			if(pEvent!=null){
				Date actionTimeRedis = pEvent.getActionTime();
				DateTime rDTime = new DateTime(actionTimeRedis.getTime());
				DateTime pDTime = new DateTime(actionTime.getTime());
				if(pDTime.isAfter(rDTime)){
					template.opsForHash().put(key, PatientEventCategory.getPatientEventCategoryByValue(categoryId).name(), domain);
					//					template.expire(key, 7, TimeUnit.DAYS);
				}
			}else{
				FriendPatientEventDay dayDatad =  friendPatientEventDayMapper.findLastByCategoryId(friendId, categoryId);
				template.opsForHash().put(key, PatientEventCategory.getPatientEventCategoryByValue(categoryId).name(), dayDatad);
				//				template.expire(key, 7, TimeUnit.DAYS);
			}
			//添加积分
			//			CreditsEvent event = new CreditsEvent(id, userId, CreditsCategory.PATIENT_EVENT_ADD);
			//			SpringContextUtil.getApplicationContext().publishEvent(event);

			syncSingletonBloodPressureInRedis(id);
			String userBloodPressureZSetKey = RedisKeyUtil.friendBloodPressureZSetKey(friendId);
			if(!template.hasKey(userBloodPressureZSetKey)){
				template.opsForZSet().add(userBloodPressureZSetKey, Integer.MAX_VALUE, -Long.MAX_VALUE*1.0D);
			}
			template.opsForZSet().add(userBloodPressureZSetKey,id,-actionTime.getTime()*1.0D);
			template.opsForZSet().removeRange(userBloodPressureZSetKey, (long) LIMIT, -1L);
		}
		return id;

	}

	/**
	 *更新 亲友健康数据-每天只记录一条
	 */
	@Override
	@Transactional
	public Integer updateFriendPatientEventDay(FriendPatientEventDay domain){

		Integer id = domain.getId();
		Date actionTime = domain.getActionTime();

		FriendPatientEventDay db = friendPatientEventDayMapper.findById(id);
		if(db==null){
			GhckException exception = new GhckException(ErrorConstant.OPERATION_ERROR.getErrorCode(),"修改的记录不存在");
			logger.info("修改健康数据失败",exception);
			throw exception;
		}
		Date dbActionTime = db.getActionTime();
		Integer friendId = domain.getFriendId();
		Integer categoryId = domain.getCategoryId();

		if(!JodaTimeUtil.isSameDay(actionTime, dbActionTime)){//那个时间不等
			FriendPatientEventDay dayData =  friendPatientEventDayMapper.findByCategoryIdAndActionTime(friendId, categoryId, actionTime);
			if(dayData!=null){ //需要删除此条记录
				Integer deleteId = dayData.getId();
				friendPatientEventDayMapper.deleteById(deleteId);
				syncSingletonBloodPressureInRedis(deleteId);
			}
		}

		friendPatientEventDayMapper.update(domain);
		//修改
		HashOperations<String, String, FriendPatientEventDay> hashOperations = template.opsForHash();
		String key = RedisKeyUtil.friendLastPatientEventMapKey(friendId);
		FriendPatientEventDay pEvent =hashOperations.get(key, PatientEventCategory.getPatientEventCategoryByValue(categoryId).name());
		if(pEvent!=null){
			Date actionTimeRedis = pEvent.getActionTime();
			DateTime rDTime = new DateTime(actionTimeRedis.getTime());
			DateTime pDTime = new DateTime(actionTime.getTime());
			if(pDTime.isAfter(rDTime)){
				template.opsForHash().put(key, PatientEventCategory.getPatientEventCategoryByValue(categoryId).name(), domain);
				//				template.expire(key, 7, TimeUnit.DAYS);
			}
		}else{
			//取最近的一条
			FriendPatientEventDay dayDatad =  friendPatientEventDayMapper.findLastByCategoryId(friendId, categoryId);
			template.opsForHash().put(key, PatientEventCategory.getPatientEventCategoryByValue(categoryId).name(), dayDatad);
			//			template.expire(key, 7, TimeUnit.DAYS);
		}
		
		if(categoryId.intValue()==PatientEventCategory.BLOOD_PRESSURE.getValue()){
			syncSingletonBloodPressureInRedis(id);
			syncBloodPressureByFriendId(friendId);
		}
		
		return 1;
	
	}

	/**
	 *根据ID查询 亲友健康数据-每天只记录一条
	 */
	public FriendPatientEventDay findFriendPatientEventDayById(Integer id){
		FriendPatientEventDay result = friendPatientEventDayMapper.findById(id);
		if(result!=null){
			result.dealWith();
		}
		return result;
	}

	/**
	 *通过query 查询 亲友健康数据-每天只记录一条
	 */
	public List<FriendPatientEventDay> findFriendPatientEventDayByQuery(FriendPatientEventDayQuery query){
		query.dealWith();
		List<FriendPatientEventDay> result = friendPatientEventDayMapper.findByQuery(query);
		if(CollectionUtils.isNotEmpty(result)) {
			for (FriendPatientEventDay friendPatientEventDay : result) {
				friendPatientEventDay.dealWith();
			}
		}
		return result;
	}	

	/**
	 *通过query count总条数
	 */
	public Integer countFriendPatientEventDayByQuery(FriendPatientEventDayQuery query){
		query.dealWith();
		Integer result = friendPatientEventDayMapper.countByQuery(query);
		return result;
	}

	/**
	 * 通过ids查询
	 */
	public List<FriendPatientEventDay> findFriendPatientEventDayByIds(List<Integer> ids){
		List<FriendPatientEventDay> result = friendPatientEventDayMapper.findByIds(ids);
		if(CollectionUtils.isNotEmpty(result)) {
			for (FriendPatientEventDay friendPatientEventDay : result) {
				friendPatientEventDay.dealWith();
			}
		}
		return result;
	}

	@Override
	public void syncBloodPressureByFriendId(Integer friendId){
		List<FriendPatientEventDay> result = findLastNByFriendId(friendId,LIMIT-1,PatientEventCategory.BLOOD_PRESSURE.getValue());
		String userBloodPressureZSetKey = RedisKeyUtil.friendBloodPressureZSetKey(friendId);
		Set<TypedTuple<Object>> set = new HashSet<TypedTuple<Object>>();
		DefaultTypedTuple<Object> tuple = new DefaultTypedTuple<Object>(Integer.MAX_VALUE, -Long.MAX_VALUE*1.0D);
		set.add(tuple);
		if (CollectionUtils.isNotEmpty(result)) {
			for (FriendPatientEventDay patientEventDay : result) {
				tuple = new DefaultTypedTuple<Object>(patientEventDay.getId(), -patientEventDay.getActionTime().getTime()*1.0D);
				set.add(tuple);
			}
			if (CollectionUtils.isNotEmpty(set)) {
				template.opsForZSet().add(userBloodPressureZSetKey, set);
			}
		}
		template.delete(userBloodPressureZSetKey);
		template.opsForZSet().add(userBloodPressureZSetKey, set);
	}

	@Override
	public void syncBloodPressureInRedis(boolean force){
		String key = RedisKeyUtil.friendBloodPressureZSetForHashKey();
		if(force || (!template.hasKey(key) )){
			template.delete(key);
			List<FriendPatientEventDay> result = findByCategoryId(PatientEventCategory.BLOOD_PRESSURE.getValue());
			Map<String, FriendPatientEventDay> map = new HashMap<String, FriendPatientEventDay>();
			if (CollectionUtils.isNotEmpty(result)) {
				for (FriendPatientEventDay patientEventDay : result) {
					String hashKey = RedisKeyUtil.domainIdKey(patientEventDay.getId());
					map.put(hashKey, patientEventDay);
				}
				if(!map.isEmpty()){
					template.opsForHash().putAll(key, map);
				}
			}
		}
	}

	@Override
	public void syncSingletonBloodPressureInRedis(Integer bloodPressureId){
		String key = RedisKeyUtil.friendBloodPressureZSetForHashKey();
		if(!template.hasKey(key)){
			syncBloodPressureInRedis(true);
		}else{
			FriendPatientEventDay patientEventDay = friendPatientEventDayMapper.findById(bloodPressureId);
			String hashKey = RedisKeyUtil.domainIdKey(bloodPressureId);
			if(patientEventDay!=null && patientEventDay.getCategoryId().intValue()==PatientEventCategory.BLOOD_PRESSURE.getValue()){
				template.opsForHash().put(key, hashKey, patientEventDay);
			}else{
				template.opsForHash().delete(key, hashKey);
			}
		}
	}

	@Override
	public List<FriendPatientEventDay> findLastNByFriendId(Integer friendId, Integer limit, Integer categoryId){
		return friendPatientEventDayMapper.findLastNByFriendId(friendId, limit, categoryId);
	}

	@Override
	public List<FriendPatientEventDay> findByCategoryId(Integer categoryId){
		return friendPatientEventDayMapper.findAllPatientEventDayByCategoryId(categoryId);
	}

	@Override
	public List<FriendPatientEventDay> findWeekBloodPress(Integer friend) {
		List<FriendPatientEventDay> result = null;
		String userBloodPressureZSetKey = RedisKeyUtil.friendBloodPressureZSetKey(friend);
		if(!template.hasKey(userBloodPressureZSetKey)){
			syncBloodPressureByFriendId(friend);
		}
		syncBloodPressureInRedis(false);
		Set<Object> idSet = template.opsForZSet().range(userBloodPressureZSetKey, 1L, -1L);
		if (CollectionUtils.isNotEmpty(idSet)) {
			List<String> list = new ArrayList<String>();
			for (Object object : idSet) {
				String hashKey = RedisKeyUtil.domainIdKey(object);
				list.add(hashKey);
			}
			if (CollectionUtils.isNotEmpty(list)) {
				String key = RedisKeyUtil.friendBloodPressureZSetForHashKey();
				HashOperations<String, String, FriendPatientEventDay> hOperations = template.opsForHash();
				result = hOperations.multiGet(key, list);
				if(CollectionUtils.isNotEmpty(result)){
					for(int i=result.size(); i>=0 ; i--){
						if(result.get(i) instanceof FriendPatientEventDay){
							result.remove(i);
						}
					}
					
					Collections.sort(result,new Comparator<FriendPatientEventDay>() {

						@Override
						public int compare(FriendPatientEventDay o1, FriendPatientEventDay o2) {
							if(o1!=null && o2!=null && o1.getActionTime()!=null && o2.getActionTime()!=null){
								return o1.getActionTime().compareTo(o2.getActionTime());
							}
							return 0;
						}
					});
				}
			}
		}
		return result;
	}
	
	/**
	 * 获取最近的健康数据
	 * 
	 */
	@Override
	public Map<String, FriendPatientEventDay> findMyLastPatient(Integer userId) {
		String key = RedisKeyUtil.friendLastPatientEventMapKey(userId);
		Map<String, FriendPatientEventDay> map = new HashMap<String, FriendPatientEventDay>();
		HashOperations<String, String, FriendPatientEventDay> hashOperations = template.opsForHash();
		if(template.hasKey(key)){
			map = hashOperations.entries(key);
		}else{
			FriendPatientEventDay BLOOD_OXYGEN = friendPatientEventDayMapper.findLastByCategoryId(userId,PatientEventCategory.BLOOD_OXYGEN.getValue());
			map.put(PatientEventCategory.BLOOD_OXYGEN.name(), BLOOD_OXYGEN);

			FriendPatientEventDay BLOOD_PRESSURE = friendPatientEventDayMapper.findLastByCategoryId(userId,PatientEventCategory.BLOOD_PRESSURE.getValue());
			map.put(PatientEventCategory.BLOOD_PRESSURE.name(), BLOOD_PRESSURE);
			
			FriendPatientEventDay BLOOD_SUGAR = friendPatientEventDayMapper.findLastByCategoryId(userId,PatientEventCategory.BLOOD_SUGAR.getValue());
			map.put(PatientEventCategory.BLOOD_SUGAR.name(), BLOOD_SUGAR);
			
			FriendPatientEventDay BODY_AGE = friendPatientEventDayMapper.findLastByCategoryId(userId,PatientEventCategory.BODY_AGE.getValue());
			map.put(PatientEventCategory.BODY_AGE.name(), BODY_AGE);
			
			FriendPatientEventDay HEART_RATE = friendPatientEventDayMapper.findLastByCategoryId(userId,PatientEventCategory.HEART_RATE.getValue());
			map.put(PatientEventCategory.HEART_RATE.name(), HEART_RATE);
			
			FriendPatientEventDay TEMPERATURE = friendPatientEventDayMapper.findLastByCategoryId(userId,PatientEventCategory.TEMPERATURE.getValue());
			map.put(PatientEventCategory.TEMPERATURE.name(), TEMPERATURE);

			template.opsForHash().putAll(key,map);
//			template.expire(key, 7, TimeUnit.DAYS);
		}
		return map;
	}

}	